<?php
#
# dmulator
#
# Copyright © 2011 Board of Regents of the Nevada System of Higher
# Education, on behalf of the University of Nevada, Las Vegas
#

/**
 * Do not include() this file directly from your own scripts. See
 * ../README.txt for information on usage of dmulator.
 */


/**
 * @return Indexed array of arrays with keys 'alias', 'name', and 'path'
 */
function dmGetCollectionList() {
	$dxml = Dmulator::getCollectionsDOMXML();
	$nodes = $dxml->documentElement->getElementsByTagName('collection');
	$collections = array();
	foreach ($nodes as $node) {
		if ($node->getAttribute("public") == 1) {
			$collections[] = array(
				'alias' => $node->getAttribute('alias'),
				'name' => $node->getAttribute('name'),
				'path' => $node->getAttribute('path')
			);
		}
	}
	return $collections;
}


/**
 * Returns the collection name and path given the collection alias. Returns 0
 * for successful lookup, -1 for no permission to read the collection, and -2
 * for lookup error. Supply an alias of '/locked' to simulate -1.
 * Verified OK with versions 4 and 5.
 *
 * @param string alias
 */
function dmGetCollectionParameters($alias, &$name, &$path) {
	if ($alias == "/locked") {
		return -1;
	}
	$dxml = Dmulator::getCollectionsDOMXML();
	foreach ($dxml->getElementsByTagName('collection') as $node) {
		if ($node->getAttribute('alias') == $alias) {
			$name = $node->getAttribute('name');
			$path = $node->getAttribute('path');
			return 0;
		}
	}
	return -2;
}


/**
 * Verified OK with version 4. See todo comment for verison 5.
 * Array keys are not necessarily returned in the same order as CONTENTdm(R).
 *
 * @param string alias
 * @return array Associative array of defined metadata field properties for the
 * collection with alias $alias
 * @todo Verify 'find', 'admin,' and 'readonly' keys for version 5, even though
 * they are not documented in the API doc.
 */
function dmGetCollectionFieldInfo($alias) {
	if (!Dmulator::collectionExists($alias)) {
		die('Error looking up collection ' . $alias);
	}
	$dxml = Dmulator::getFieldsDOMXML();
	$node = $dxml->documentElement->getElementsByTagName('fields')->item(0);
	$fields = array();
	foreach ($node->getElementsByTagName('field') as $fnode) {
		$f = array();
		$f['name'] = $fnode->getAttribute('name');
		$f['nick'] = $fnode->getAttribute('nick');
		$f['type'] = $fnode->getAttribute('type');
		$f['size'] = $fnode->getAttribute('size');
		$f['search'] = $fnode->getAttribute('search');
		$f['hide'] = $fnode->getAttribute('hide');
		$f['vocab'] = $fnode->getAttribute('vocab');
		$f['dc'] = $fnode->getAttribute('dc');
		$f['find'] = 'BLANK';
		$f['req'] = $fnode->getAttribute('req');
		$f['vocdb'] = '';
		$f['admin'] = 0;
		$f['readonly'] = 0;
		$fields[] = $f;
	}
	return $fields;
}


/**
 * Verified OK with versions 4 and 5.
 *
 * @param string lang No langs are currently supported; if this is supplied,
 * fopen will throw a warning and the script will exit, as it would in the real
 * API
 * @return [array|void]
 */
function dmGetDublinCoreFieldInfo($lang = null) {
	if (!empty($lang)) {
		fopen('/jeremiah/was/a/bullfrog', 'r');
		die('Error opening Dublin Core configuration file');
	}
	$dxml = Dmulator::getFieldsDOMXML();
	$node = $dxml->documentElement->getElementsByTagName('dcMap')->item(0);
	$fields = array();
	foreach ($node->getElementsByTagName('field') as $fnode) {
		$f = array();
		$f['name'] = $fnode->getAttribute('name');
		$f['nick'] = $fnode->getAttribute('nick');
		$f['type'] = $fnode->getAttribute('type');
		$fields[] = $f;
	}
	return $fields;
}


/**
 * Verified OK with versions 4 and 5, although it is deprecated in version 5.
 *
 * @param string alias
 * @param int enabled Full resolution is disabled (0) or enabled (1)
 * @param int public Private (0) or public (1)
 * @param string volprefix Volume prefix
 * @param string volsize Volume size
 * @param string displaysize Default display image dimensions (width x height in
 * pixels)
 * @param string archivesize Default archive image display dimensions
 * (width x height in pixels)
 */
function dmGetCollectionFullResInfo($alias, &$enabled, &$public,
		&$volprefix, &$volsize, &$displaysize, &$archivesize) {
	if (!Dmulator::collectionExists($alias)) {
		die('Error looking up collection ' . $alias);
	}

	$enabled = 0;
	$dxml = Dmulator::getCollectionsDOMXML();
	foreach ($dxml->getElementsByTagName('collection') as $node) {
		if ($node->getAttribute('alias') == $alias) {
			if ($node->getAttribute('fullRes')) {
				$enabled = 1;
				$volprefix = $alias;
				$volsize = '650MB';
				$displaysize = '1024x768';
				$archivesize = '1600x1200';
				$public = $node->getAttribute('public');
			}
			return;
		}
	}
	return;
}


/**
 * @param string alias
 * @param int enabled
 * @param int public
 * @param string volprefix
 * @param string volsize
 * @param string oclcsym
 * @todo Find out if disabling archiving affects the other variables
 */
function dmGetCollectionArchivalInfo($alias, &$enabled, &$public, &$volprefix,
		&$volsize, &$oclcsym) {
	if (!Dmulator::collectionExists($alias)) {
		die("Error looking up collection " . $alias);
	}
	$dxml = Dmulator::getCollectionsDOMXML();
	$dxp = new DOMXPath($dxml);
	$result = $dxp->query(
		sprintf("collection[@alias = '%s']/archival", $alias)
	)->item(0);

	$enabled = (int) $result->getAttribute('enabled');
	$public = (int) $result->getAttribute('public');
	$volprefix = $result->getAttribute('volprefix');
	$volsize = $result->getAttribute('volsize');
	$oclcsym = $result->getAttribute('oclcsym');
}


/**
 * Verified OK with version 5 (not available in 4).
 *
 * @param string alias
 * @param int enabled
 * @param [page|file] type
 * @param string pagetext The text string used to name compound object pages
 * @param int start The starting page number to use when naming pages
 */
function dmGetCollectionPDFInfo($alias, &$enabled, &$type, &$pagetext, &$start) {
	if (!Dmulator::collectionExists($alias)) {
		die("Error looking up collection " . $alias);
	}
	$dxml = Dmulator::getCollectionsDOMXML();
	$dxp = new DOMXPath($dxml);
	$result = $dxp->query(
		sprintf("collection[@alias = '%s']/pdf", $alias)
	)->item(0);

	$enabled = (int) $result->getAttribute('enabled');
	$type = $result->getAttribute('type');
	if ($type == 'page') {
		$pagetext = $result->getAttribute('pagetext');
	}
	$start = (int) $result->getAttribute('start');
}


/**
 * @param string alias
 * @param int enabled
 * @param [jp2|jpg] format
 * @param [0|1]lossy
 * @param [ratio|quality] comptype
 * @param [10..100] ratio
 * @param [Minimum|Medium|High|Maximum] quality
 * @param [512x512|1024x1024|2048x2048] tile
 * @param int levels 4 to 10, or 0 for automatic
 * @param [10..30] layers
 * @param string jpgdim Dimensions of the generated image
 * @param int jpgquality 1 to 100
 * @todo Verify output
 */
function dmGetCollectionDisplayImageSettings($alias, &$enabled, &$format,
		&$lossy, &$comptype, &$ratio, &$quality, &$tile, &$levels, &$layers,
		&$jpgdim, &$jpgquality) {
	if (!Dmulator::collectionExists($alias)) {
		die("Error looking up collection " . $alias);
	}
	$dxml = Dmulator::getCollectionsDOMXML();
	$dxp = new DOMXPath($dxml);
	$result = $dxp->query(
		sprintf("collection[@alias = '%s']/image", $alias)
	)->item(0);

	$enabled = (int) $result->getAttribute('enabled');
	$format = $result->getAttribute('format');
	if ($format == 'jp2') {
		$lossy = $result->getAttribute('lossy');
		$comptype = $result->getAttribute('comptype');
		if ($comptype == 'ratio') {
			$ratio = $result->getAttribute('ratio');
		}
		$quality = $result->getAttribute('quality');
		$tile = $result->getAttribute('tile');
		$levels = $result->getAttribute('levels');
		$layers = $result->getAttribute('layers');
	} else {
		$jpgdim = $result->getAttribute('jpgdim');
		$jpgquality = $result->getAttribute('jpgquality');
	}
}


/**
 * @deprecated Not implemented yet
 */
function dmGetLocale() {}


/**
 * @param string alias
 * @param string volname
 * @param string location
 * @todo Verify output
 */
function dmGetCollectionFullResVolumeInfo($alias, $volname, &$location) {
	if (!Dmulator::collectionExists($alias)) {
		die("Error looking up collection " . $alias);
	}
	$dxml = Dmulator::getCollectionsDOMXML();
	$dxp = new DOMXPath($dxml);
	$result = $dxp->query(
		sprintf("collection[@alias = '%s']/archival", $alias)
	)->item(0);
	$location = $result->getAttribute('location');
}


/**
 * Verified OK in versions 4 and 5.
 *
 * @param string alias
 * @param string field Field nick
 * @param int forcedict Currently ignored
 * @param int forcefullvoc Currently ignored
 * @todo Enable $forcedict, and $forcefullvoc
 * @return Indexed array of strings, or an empty array if the given field is
 * invalid
 */
function dmGetCollectionFieldVocabulary($alias, $field, $forcedict,
		$forcefullvoc) {
	if (!Dmulator::collectionExists($alias)) {
		die("Error looking up collection " . $alias);
	}
	$result = dmGetCollectionFieldInfo($alias);
	$found = 0;
	foreach ($result as $cf) {
		if ($cf['nick'] == $field) {
			$found = 1;
			break;
		}
	}
	if (!$found) {
		return array();
	}

	$dxml = Dmulator::getVocabularyDOMXML();
	$terms = array();
	foreach ($dxml->documentElement->getElementsByTagName('term') as $node) {
		$terms[] = $node->nodeValue;
	}
	return $terms;
}


/**
 * Currently all the return values are hard-coded.
 * Verified OK in versions 4 and 5.
 *
 * @param string alias
 * @param int enabled
 * @param int minjpegdim
 * @param array zoomlevels
 * @param array maxderivedimg
 * @param array viewer
 * @param array docviewer
 * @param array compareviewer
 * @param array slideshowviewer
 */
function dmGetCollectionImageSettings($alias, &$enabled, &$minjpegdim,
		&$zoomlevels, &$maxderivedimg, &$viewer, &$docviewer, &$compareviewer,
		&$slideshowviewer) {
	if (!Dmulator::collectionExists($alias)) {
		die('Error looking up collection ' . $alias);
	}

	$enabled = 1;
	$minjpegdim = 500;
	$zoomlevels = array(1.5625, 3.125, 6.25, 12.5, 25, 50, 100);
	$maxderivedimg = array(
		'width' => 600,
		'height' => 600
	);
	$viewer = array(
		'thumbnail' => 1,
		'width' => 600,
		'height' => 600,
		'scale' => 'W'
	);
	$docviewer = array(
		'thumbnail' => 0,
		'width' => 750,
		'height' => 'A',
		'scale' => 'W',
		'nomenuwidth' => 968,
		'nomenuheight' => 'A',
		'nomenuscale' => 'W'
	);
	$compareviewer = array(
		'thumbnail' => 0,
		'width' => 500,
		'height' => 500,
		'scale' => 'W'
	);
	$slideshowviewer = array(
		'thumbnail' => 0,
		'width' => 600,
		'height' => 600,
		'scale' => 'W'
	);
}


/**
 * Verified OK in versions 4 and 5.
 * Note: Returned XML is valid, but empty tags <strong>may</strong> be
 * self-closing and whitespace <strong>may</strong> be different than that
 * returned by CONTENTdm(R). If you are using an XML-compliant parser, it won't
 * matter.
 *
 * @param string alias
 * @param int ptr
 * @param string xmlbuffer
 * @return int 0 if successful, -1 if not
 */
function dmGetItemInfo($alias, $ptr, &$xmlbuffer) {
	if (!Dmulator::collectionExists($alias)) {
		return -1;
	}
	if ($alias == "/dynamic") {
		$xmlbuffer = '<xml>
			<title>Inside of a black hole</title>
			<subjec></subjec>
			<descri>Photograph taken of the inside of a black hole. The
			singularity is visible in the upper right hand corner.</descri>
			<creato>A very brave photographer</creato>
			<publis></publis>
			<contri></contri>
			<date>1997-07-31</date>
			<type></type>
			<format></format>
			<identi>sample identifier</identi>
			<source></source>
			<langua></langua>
			<relati></relati>
			<covera></covera>
			<rights>Public domain</rights>
			<audien></audien>
			<fullrs></fullrs>
			<find>dynamic.jpg</find>
			<dmaccess></dmaccess>
			<dmcreated>2007-07-31</dmcreated>
			<dmmodified>2010-01-22</dmmodified>
			<dmoclcno></dmoclcno>
		</xml>';
		return 0;
	} else {
		$dxml = clone Dmulator::getObjectsDOMXML();
		$dxp = new DOMXPath($dxml);
		$result = $dxp->query(
			sprintf('//xml[@ptr = %d and @alias = "%s"]', $ptr, $alias)
		);
		if ($result->length > 0) {
			// transform result to the proper format
			$result->item(0)->appendChild($dxml->createElement("dmrecord", $ptr));
			$result->item(0)->removeAttribute("alias");
			$result->item(0)->removeAttribute("ptr");
			$dxml->formatOutput = true;
			$xmlbuffer = $dxml->saveXML($result->item(0));
			return 0;
		}
	}
	$xmlbuffer = "";
	return -1;
}


/**
 * Note: Returned XML is valid, but empty tags are self-closing and whitespace
 * indentation is different than that returned by CONTENTdm(R).
 *
 * @param string alias
 * @param int ptr
 * @param string xmlbuffer
 * @return int 0 if successful, -1 if not
 */
function dmGetCompoundObjectInfo($alias, $ptr, &$xmlbuffer) {
	if (!Dmulator::collectionExists($alias)) {
		return -1;
	}
	$dxml = clone Dmulator::getObjectsDOMXML();
	$dxp = new DOMXPath($dxml);

	// make sure the object exists
	$result = $dxp->query(
		sprintf('//xml[@ptr = %d and @alias = "%s"]', $ptr, $alias));
	if ($result->length < 1) {
		return -1;
	}

	$result = $dxp->query(sprintf('//cpd[@ptr = %d]', $ptr));
	if ($result->length > 0) {
		$result->item(0)->removeAttribute('ptr');
		$dxml->formatOutput = true;
		$xmlbuffer = "<?xml version=\"1.0\"?>\n"
			. $dxml->saveXML($result->item(0));
	}
	return 0;
}


/**
 * Note: Returned XML is valid, but empty tags are self-closing and whitespace
 * indentation is different than that returned by CONTENTdm(R).
 *
 * @param string alias
 * @param int ptr
 * @param string filename Full path filename of the image file
 * @param [jpg|gif|jp2|tif|tiff] type Image type
 * @param int width
 * @param int height
 * @return 0 for a successful call; -1 if there is no permission to access the
 * item, which, in Dmulator, will never be the case
 */
function dmGetImageInfo($alias, $ptr, &$filename, &$type, &$width, &$height) {
	if (!Dmulator::collectionExists($alias)) {
		return -1;
	}
	if ($alias == "/dynamic") {
		$filename = sprintf('/usr/local/Content5%s/image/%s.jpg',
			$alias,
			$ptr);
		$type = "jpg";
		$width = 800;
		$height = 600;
		return 0;
	}
	$dxml = Dmulator::getObjectsDOMXML();
	$dxp = new DOMXPath($dxml);
	// make sure the object exists
	$result = $dxp->query(
		sprintf('//xml[@ptr = %d and @alias = "%s"]', $ptr, $alias));
	if ($result->length < 1) {
		return -1;
	}

	$result = $dxp->query(
		sprintf('//xml[@ptr = %d and @alias = "%s"]/find', $ptr, $alias));
	if ($result->length > 0) {
		$filename = sprintf('/usr/local/Content5%s/image/%s',
			$alias,
			$result->item(0)->nodeValue);
		$type = "jpg";
		$width = 640;
		$height = 480;
		return 0;
	}
}


/**
 * @deprecated Not implemented yet
 */
function dmGetFavorites() {}


/**
 * @deprecated Not implemented yet
 */
function dmAddFavorite() {}


/**
 * @deprecated Untested
 */
function dmDeleteFavorite(array $favorites) {
	if (array_key_exists('BUF', $_COOKIE)) {
		$buf = $_COOKIE['BUF'];
		foreach ($favorites as $f) {
			$buf = str_replace(
				sprintf('%s<%s>', $f['collection'], $f['pointer']),
				'',
				$buf
			);
		}
		setrawcookie('BUF', $buf, null, '/');
	}
}


/**
 * @deprecated Not implemented yet
 */
function dmMoveFavorite() {}


/**
 * @param array alias Array of one or more collection aliases. Put "all" at
 * 	element 1 to search all collections.
 * @param array searchstring Array of arrays with string, field, and
 * 	mode keys. Mode is currently ignored.
 * @param array fields Array of field nicks to include in the results
 * 	(maximum of 5)
 * @param string sortby Ignored
 * @param int maxrecs
 * @param int start
 * @param int total
 * @param int suppress Whether to suppress compound object pages;
 * 	1 for yes, 0 for no
 * @param int docptr Ignored
 * @param string suggest Whether to return a search suggestion
 * @param string facet Whether to return facets
 * @return array
 * @todo Implement missing functionality
 */
function dmQuery($alias, $searchstring, $fields,
		$sortby, $maxrecs, $start, &$total, $suppress = 0,
		$docptr = -1, &$suggest = 0, &$facet = "") {
	$dxp = new DOMXPath(Dmulator::getObjectsDOMXML());

	// $alias must be an array. If there is a mix of valid and invalid
	// aliases, the invalid ones will be discarded. If all are invalid,
	// the script will terminate.
	$ok_count = 0;
	$msg = "";
	if (!is_array($alias)) {
		die("Error looking up collection /");
	}
	foreach ($alias as $a) {
		if ($a === "") {
			$msg = "Error looking up collection";
		} else if (is_null($a)) {
			$msg = "Error, no collections specified";
		} else if (!Dmulator::collectionExists($a)) {
			$msg = "Error looking up collection " . $a;
		}
		$ok_count++;
	}
	if (!$ok_count) {
		die($msg);
	}

	// build xpath fragment for alias searching
	$xpath_alias = ($alias[0] == "all")
		? ""
		: 'and (../@alias = "' . implode('" or ../@alias = "', $alias) . '")';

	// build xpath fragment for string searching
	$xpath = null;
	if (!count($searchstring)) {
		$xpath = sprintf('//xml/*[1 = 1 %s]/..', $xpath_alias);
	} else {
		$valid_modes = array('all', 'exact', 'any', 'none');
		foreach ($searchstring as $str) {
			// ignore any search strings that have invalid matching modes
			if (!in_array($str['mode'], $valid_modes)) {
				continue;
			}
			$xpath = sprintf('//xml/%s[contains(.,"%s") %s]/..',
				(in_array($str['field'], Dmulator::getFieldNicks()))
					? $str['field'] : '*',
				Dmulator::xmlentities($str['string']),
				$xpath_alias);
		}
	}

	// find compound object pages
	$cpd_result = $dxp->query("//cpd/page/pageptr");
	$cpd_page_ptrs = array();
	foreach ($cpd_result as $ptr) {
		$cpd_page_ptrs[] = $ptr->nodeValue;
	}

	// build results list
	$results = array();
	$length = 0;
	// if the /dynamic collection, generate random results
	if ($alias[0] == "/dynamic") {
		// this is rather inefficient, but who cares
		for ($i = 1000; $i < 2000; $i++) {
			$return = array();
			$return['collection'] = $alias[0];
			$return['pointer'] = $i;
			$return['filetype'] = "jpg";
			$return['parentobject'] = -1;
			for ($j = 0; $j < count($fields); $j++) {
				$return[$fields[$j]] = md5($i);
			}
			$results[] = $return;
			$length++;
		}
	} else {
		$cpd_obj_ptrs = array();
		if ($docptr > -1) {
			$cpd_result = $dxp->query(
					sprintf("//cpd[@ptr = '%s']/page/pageptr", $docptr));
			foreach ($cpd_result as $ptr) {
				$cpd_obj_ptrs[] = $ptr->nodeValue;
			}
		}

		$xpath = $xpath ? $xpath : "//xml";
		foreach ($dxp->query($xpath) as $node) {
			$ptr = $node->getAttribute("ptr");
			if ($suppress && in_array($ptr, $cpd_page_ptrs)) {
				continue;
			}
			if ($docptr > -1 && !in_array($ptr, $cpd_obj_ptrs)) {
				continue;
			}
			$return = array();
			$return['collection'] = $node->getAttribute("alias");
			$return['pointer'] = $ptr;
			$return['filetype'] = "jpg";
			$return['parentobject'] = -1;
			for ($i = 0; $i < count($fields); $i++) {
				$return[$fields[$i]] = null;
				if ($i < 5) {
					$tmp = $node->getElementsByTagName($fields[$i]);
					if ($tmp->length) {
						$return[$fields[$i]] = $tmp->item(0)->nodeValue;
					}
				}
			}
			$results[] = $return;
			$length++;
		}
	}

	// handle facets
	if ($facet) {
		$dxml = new DOMDocument("1.0", "utf-8");
		$dxml->loadXML("<facet/>");
		foreach (explode(":", $facet) as $term) {
			$dxml->documentElement->appendChild(
				$dxml->createElement("label", $term));
			$dxml->documentElement->appendChild(
				$dxml->createElement("name", "Sample Facet Term"));
			$dxml->documentElement->appendChild(
				$dxml->createElement("count", 2));
		}
		$facet = $dxml->saveXML($dxml->documentElement);
	}

	$total = ($suppress) ? count($results) : $length;
	$suggest = ($suggest) ? "dmulator demo suggestion" : $suggest;

	return array_slice($results, $start - 1, $maxrecs);
}


/**
 * @param string alias
 * @param int ptr
 * @param string path The collection path as returned by
 * dmGetCollectionParameters()
 * @return int
 */
function GetParent($alias, $ptr, $path) {
	$dxml = Dmulator::getObjectsDOMXML();
	$dxp = new DOMXPath($dxml);

	// make sure the object exists
	$result = $dxp->query(
		sprintf('//xml[@ptr = %d and @alias = "%s"]', $ptr, $alias));
	if ($result->length < 1) {
		return -1;
	}

	// make sure the path exists
	$dxml2 = Dmulator::getCollectionsDOMXML();
	$dxp2 = new DOMXPath($dxml2);
	$result = $dxp2->query(
		sprintf('//collection[@path = "%s"]', $path));
	if ($result->length < 1) {
		return -1;
	}

	$result = $dxp->query(
		sprintf('//cpd[page/pageptr = %d]/@ptr', $ptr));
	return ($result->length > 0) ? $result->item(0)->nodeValue : -1;
}


/**
 * An undocumented API method that is required by the default CONTENTdm(R)
 * templates. According to its inline documentation, it returns -1 for
 * "no permission," 1 for "full access," and 2 for "metadata only." This
 * emulated version returns 1, always. Also, unlike the default version which
 * requires php_dmoprmod.dll (Windows) or dmopr.so (Unix) and will fail if they
 * are not present, this does not try to load any CONTENTdm(R) PHP modules.
 *
 * @param string path
 * @param int ptr
 * @param string xml
 * @return int
 * @deprecated
 * @todo Eliminate ReadItemDesc()
 */
function ReadItemDesc($path, $ptr, &$xml) {
	return 1;
}


/**
 * An undocumented API method that is required by the default CONTENTdm(R)
 * templates. There is no inline documentation for this method. I guess it is
 * supposed to return an XML node value by parsing the XML as a string. This
 * emulated version simply returns an empty string.
 *
 * @param string tag
 * @param string xml
 * @return string
 * @deprecated
 * @todo Eliminate GetXMLField()
 */
function GetXMLField($tag, $xml) {
	return '';
}
